Skip to content
This repository was archived by the owner on Apr 3, 2019. It is now read-only.

Conversation

@eoger
Copy link
Contributor

@eoger eoger commented Mar 16, 2018

Connects to mozilla/fxa-auth-server#2320 (please read for context).

  • When fetching a device infos, we want to query devices and devicesCapabilities at the same time.
    Since MySQL doesn't have an Array type, we pull the device capabilities using a GROUP_CONCAT and convert them to an array in JS.
  • For creating or updating a device, we have no choice but to make multiple queries in the same transaction (see the new writeMultiple method).
  • We maintain a "capability name" <> "capability id" mapping. In practice this is the CAPABILITIES array in mysql.js.
  • If device.capabilities is supplied in updateDevice, it is implied that we are replacing the current device capabilities.

@ghost ghost assigned eoger Mar 16, 2018
@ghost ghost added the waffle:active label Mar 16, 2018
Copy link
Contributor

@philbooth philbooth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eoger, I think this looks good! Shout if you disagree with any of the comments, I seem to be having an opinionated day today!

.gitignore Outdated
@@ -1,3 +1,4 @@
.DS_Store
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, but IIRC we prefer to keep platform-specific ignores out of the project .gitignore and ask people to add it to their own ~/.gitignore_global instead.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please use a global git ignore

const index = CAPABILITIES.indexOf(c)
if (index !== -1) {
acc.push({sql: ADD_CAPABILITY, params: [uid, deviceId, index]})
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder whether it might be more helpful to clients to fail the request completely if they submit an unrecognised capability, rather than dropping it silently?

Or do you envisage that validation taking place in the auth server? If that's the case it seems unfortunate that we have to define the valid capability strings in two separate repos. Instead, maybe the auth server could take responsibility for mapping between the capability string and its enumeration, leaving this repo just handling integers?

deviceId BINARY(16) NOT NULL,
capability TINYINT UNSIGNED NOT NULL,
PRIMARY KEY (uid, deviceId, capability),
FOREIGN KEY (uid, deviceId) REFERENCES devices(uid, id) ON DELETE CASCADE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sigh, I should have used ON DELETE CASCADE on the devices table itself. And unverifiedTokens. 🤦‍♂️

lib/db/mysql.js Outdated
return query(connection, sql, params)
.then(
function (result) {
log.trace('MySql.write', { sql, result })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This trace message should be MySql.writeMultiple presumably?

lib/db/mysql.js Outdated
function (result) {
log.trace('MySql.write', { sql, result })
if (resultHandler) {
return resultHandler(i, result)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just thinking out loud, ignore me if it's rubbish, but...

Instead of passing in one resultHandler and then giving it an index so it knows which query it's being invoked for, would it be a little bit cleaner if resultHandler was a property of the objects inside the queries array? So each query can have a dedicated result handler that doesn't need to check the value of i?

lib/db/mysql.js Outdated
}

// Select : devices d, sessionTokens s, accounts a
// Since MySQL doesn't have an ARRAY type, we store arrays as comma-separated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More bike-shedding, but this comment reads a little bit inaccurately to me. Unless I misunderstand, we're not storing them as comma-separated values, they're stored as rows in devicesCapabilities. They're just returned from queries as comma-separated values, right?

lib/db/mysql.js Outdated
}

// Enum capability name -> capability id (index)
const CAPABILITIES = ['pushbox']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also define a const CAPABILITY_IDS at this point, containing the inverse mapping from string to integer? It would save us having to rely on indexOf in the rest of the code, and could be generated with CAPABILITIES.reduce so no need to actually define anything really.

lib/db/mysql.js Outdated
const capabilitiesQueries = []
if (deviceInfo.hasOwnProperty('capabilities')) {
capabilitiesQueries.push({sql: PURGE_CAPABILITIES, params: [uid, deviceId]})
addLegalCapabilities(uid, deviceId, deviceInfo, capabilitiesQueries)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bike-shedding but I think, on balance, I don't like this pattern of passing in the array and modifying it (or not) inside addLegalCapabilities. It's kind of small-scale action-at-a-distance.

Would it be cleaner if addLegalCapabilities always returns a fresh array and the caller can then be explicit about joining them with capabilitiesQueries.concat if it needs to?

IN `inCapability` TINYINT UNSIGNED
)
BEGIN
INSERT INTO devicesCapabilities(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even more bike-shedding (sorry)... are we sure about the plural on the devices part here? deviceCapabilities sounds more natural to me.

@eoger
Copy link
Contributor Author

eoger commented Mar 16, 2018

Thank you @philbooth that was really useful feedback!
I've updated my patch with your comments and added tests.

['totp-2fa', 2] // TOTP code
])

// If you modify one of these maps, modify the other.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine for now but I think, if the capabilities list gets longer in the future, we should definitely switch to generating one of these programmatically so that there's only one definition to maintain.

Copy link
Contributor

@philbooth philbooth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Splendid! Let's also add the new error information to the docs for createDevice and updateDevice, then get this merged!

"callbackAuthKey": "w3b14Zjc-Afj2SDOLOyong",
"callbackIsExpired": false
"callbackIsExpired": false,
"capabilities": ["pushbox"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also document the new error response in the section below?

"callbackAuthKey": "w3b14Zjc-Afj2SDOLOyong",
"callbackIsExpired": false
"callbackIsExpired": false,
"capabilities": ["pushbox"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also document the new error response in the section below?

* `callbackAuthKey` (string):
Auth key for push service
* `capabilities` (array):
Array of strings describing the current device capabilities
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also document the new error under Rejects with?

* `callbackAuthKey` (string):
Auth key for push service
* `capabilities` (array):
Array of strings describing the current device capabilities
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also document the new error under Rejects with?

@eoger eoger force-pushed the devices-capabilities branch from 2e41a11 to 9577f83 Compare March 19, 2018 14:10
@eoger
Copy link
Contributor Author

eoger commented Mar 19, 2018

All done, thank you!

@eoger eoger merged commit 4808a1c into master Mar 19, 2018
@ghost ghost removed the waffle:review label Mar 19, 2018
@eoger eoger deleted the devices-capabilities branch March 19, 2018 14:28
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants